package org.example.dao;

import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import org.example.entity.AbstractModel;
import org.example.entity.Model;
import org.springframework.transaction.annotation.Transactional;


public abstract class AbstractDao<T extends AbstractModel> implements Dao<T>
{

  @PersistenceContext(unitName = "helloWorldPU") private EntityManager defaultEntityManager;
  private Class<T> modelClass;


  public AbstractDao(Class<T> modelClass)
  {
    //super();
    this.modelClass = modelClass;
  }


  protected EntityManager entityManager()
  {
    return defaultEntityManager;
  }


  protected T createOrUpdate(T model)
  {
    // long t0 = System.currentTimeMillis();
    if (model.getId() == 0)
    {
      entityManager().persist(model);
    }
    else
    {
      entityManager().merge(model);
    }
    // log().info("spent: {}", (System.currentTimeMillis()-t0));
    return model;
  }



  /* (non-Javadoc)
   * @see org.example.dao.Dao#create(T)
   */
  @Transactional
  public T create(T model)
  {
    return this.createOrUpdate(model);
  }



  /* (non-Javadoc)
   * @see org.example.dao.Dao#update(T)
   */
  @Transactional
  public T update(T model)
  {
    return this.createOrUpdate(model);
  }



  /* (non-Javadoc)
   * @see org.example.dao.Dao#delete(int)
   */
  @Transactional
  public boolean delete(int modelId)
  {
    T model = this.find(modelId);
    return this.delete(model);
  }



  /* (non-Javadoc)
   * @see org.example.dao.Dao#delete(T)
   */
  @Transactional
  public boolean delete(T model)
  {
    if (model != null)
    {
      entityManager().remove(model);
      return true;
    }
    else
    {
      // log().warn("object {} does not exist", model);
      return false;
    }
  }



  /* (non-Javadoc)
   * @see org.example.dao.Dao#find(int)
   */
  @Transactional(readOnly = true)
  public T find(int modelId)
  {
    T model = entityManager().find(modelClass, modelId);
    return model;
  }



  /* (non-Javadoc)
   * @see org.example.dao.Dao#mandatoryId(int)
   */
  @Transactional(readOnly = true)
  public T mandatoryId(int modelId)
  {
    T model = find(modelId);
    if (model == null)
    {
      throw new RuntimeException("The required model with the specified id does not exist: " + modelId);
    }
    return model;
  }



  /* (non-Javadoc)
   * @see org.example.dao.Dao#findAll()
   */
  @Transactional(readOnly = true)
  public List<T> findAll()
  {
    CriteriaBuilder cb = entityManager().getCriteriaBuilder();
    CriteriaQuery<T> query = cb.createQuery(modelClass);
    Root<T> root = query.from(modelClass);
    query.select(root);
    List<T> list = entityManager().createQuery(query).getResultList();
    return list;
  }
}
